package com.treeroot.treenet.protocol.ws;

import com.treeroot.treenet.enums.ProtocolTypeMenu;
import com.treeroot.treenet.protocol.BaseProtocolHandle;
import com.treeroot.treenet.protocol.ConnectDataCallback;
import com.treeroot.treenet.protocol.abs.tcp.AbsTcpConnection;
import com.treeroot.treenet.protocol.abs.tcp.session.TcpSession;
import com.treeroot.treenet.strategy.ProtocolStrategyHandle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.function.Consumer;

import static com.treeroot.treenet.common.utils.WsUtils.calculateWebSocketAccept;
import static com.treeroot.treenet.common.utils.WsUtils.extractWebSocketKey;

/**
 * Create By TreeRoot
 * Create Time: 2023/12/18 03:44
 * Class Name: HandleWebSocketProtocol
 * Description:
 * Websocket协议处理器
 *
 * @author TreeRoot
 */
public class HandleWebSocketProtocol extends AbsTcpConnection implements BaseProtocolHandle<TcpSession>,
        ProtocolStrategyHandle {

    private static final Logger log = LoggerFactory.getLogger(HandleWebSocketProtocol.class);
    private static final byte OPCODE_TEXT = (byte) 0x81;


    @Override
    public void handleRequest(ProtocolTypeMenu protocolType) {
        log.info("开始处理 HandleWebsocketRequest.handleRequest");
        this.handleRequest(this::acceptConnection);
        log.info("结束处理 HandleTcpRequest.handleRequest");
    }


    @Override
    public void acceptConnection(TcpSession baseTcpVo) {
        Selector selector = baseTcpVo.getSelector();
        ServerSocketChannel serverSocketChannel = baseTcpVo.getServerSocketChannel();
        //1.接受客户端连接请求
        try (SocketChannel socketChannel = serverSocketChannel.accept()) {
            if (socketChannel != null) {
                log.info("接受到客户端连接: {}", socketChannel.getRemoteAddress());
                //2.设置为非阻塞模式
                socketChannel.configureBlocking(false);
                //3.注册选择器，指定监听事件为 OP_READ(读取数据 读就绪)
                socketChannel.register(selector, SelectionKey.OP_READ);
                readDataFromClient(socketChannel.keyFor(selector),()-> System.out.println("客户端关闭连接"));
            }
        } catch (Exception e) {
            log.warn("HandleTcpRequest.handleRequest: 无法创建 ServerSocket", e);
        }
    }

    @Override
    public void readDataFromClient(SelectionKey key, ConnectDataCallback connectDataCallback) {
        try {
            SocketChannel socketChannel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int bytesRead = socketChannel.read(buffer);
            if (bytesRead == -1) {
                socketChannel.close();
                key.cancel();
                return;
            }
            buffer.flip();
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            String data = new String(bytes, StandardCharsets.UTF_8);
            if (!data.startsWith("GET") || !data.contains("Upgrade: websocket")) {
                // 不是WebSocket升级请求，关闭连接
                socketChannel.close();
                key.cancel();
                return;
            }
            // 执行WebSocket握手
            String webSocketKey = extractWebSocketKey(data);
            String webSocketAccept = calculateWebSocketAccept(webSocketKey);
            String response = "HTTP/1.1 101 Switching Protocols\r\n"
                    + "Upgrade: websocket\r\n"
                    + "Connection: Upgrade\r\n"
                    + "Sec-WebSocket-Accept: " + webSocketAccept + "\r\n\r\n";
            ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes(StandardCharsets.UTF_8));
            socketChannel.write(responseBuffer);
            log.info("WebSocket握手成功。WebSocket upgraded for client{}", socketChannel.getRemoteAddress());
            this.roleToWebSocket(key);
        } catch (Exception e) {
            log.error("HandleWebSocketProtocol.readDataFromClient: ", e);
        } finally {
            log.info("HandleWebsocketRequest.handleRequest: 本次处理结束");
        }
    }

    public void roleToWebSocket(SelectionKey key) {
        log.info("HandleWebSocketProtocol.roleToWebSocket: 正在启动角色到 WebSocket 的转换");

        try (SocketChannel socketChannel = getSocketChannel(key)) {
            // 向客户端发送初始消息
            String msg = "Hello, I am WebSocket Server";
            byte[] payload = msg.getBytes();
            byte[] frameHeader = new byte[2 + payload.length];
            frameHeader[0] = OPCODE_TEXT;
            frameHeader[1] = (byte) payload.length;
            System.arraycopy(payload, 0, frameHeader, 2, payload.length);
            ByteBuffer buffer = ByteBuffer.wrap(frameHeader);
            socketChannel.write(buffer);

            // 创建一个线程来持续发送数据
            new Thread(() -> {
                boolean toggle = true; // 用于切换消息

                while (true) {
                    try {
                        String action;
                        if (toggle) {
                            action = "worksheet_submit_requesting";
                        } else {
                            action = "worksheet_submitted";
                        }
                        toggle = !toggle; // 切换消息

                        sendJsonToClient(socketChannel,
                                "{\n" +
                                        "  \"action\": \"" + action + "\",\n" + // 使用动态 action
                                        "  \"msg\": \"我是大帅哥\",\n" +
                                        "  \"title\": \"我是帅哥\",\n" +
                                        "  \"type\": \"error\"\n" +
                                        "}"
                        );
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        log.info("Error in sending data thread", e);
                        break;
                    }
                }
            }).start();

            // 监听客户端消息
            while (true) {
                buffer.clear();
                int bytesRead = socketChannel.read(buffer);

                if (bytesRead == -1) {
                    log.info("HandleWebSocketProtocol.roleToWebSocket: 客户端断开连接");
                    socketChannel.close();
                    key.cancel();
                    break;
                }

                buffer.flip();
                if (buffer.hasRemaining()) {
                    byte[] bytes = new byte[buffer.remaining()];
                    buffer.get(bytes);
                    String receivedMessage = new String(bytes);
                    log.info("HandleWebSocketProtocol.roleToWebSocket: 收到客户端消息: {}", receivedMessage);
                }
            }
        } catch (IOException e) {
            log.info("HandleWebSocketProtocol.roleToWebSocket: ", e);
        }
    }

    private SocketChannel getSocketChannel(SelectionKey key) throws IOException {
        return (SocketChannel) key.channel();
    }

    private void sendJsonToClient(SocketChannel socketChannel, String json) {
        try {
            byte[] payload = json.getBytes();
            byte[] frameHeader = new byte[2 + payload.length];
            frameHeader[0] = OPCODE_TEXT;
            frameHeader[1] = (byte) payload.length;
            System.arraycopy(payload, 0, frameHeader, 2, payload.length);
            ByteBuffer buffer = ByteBuffer.wrap(frameHeader);
            if (socketChannel.isConnected()) {
                socketChannel.write(buffer);
            }
        } catch (IOException e) {
            log.info("HandleWebSocketProtocol.sendJsonToClient: ", e);
        }
    }

    @Override
    public void handleRequest(Consumer<TcpSession> acceptConnectionConsumer) {
        super.handleRequest(acceptConnectionConsumer);

    }

    @Override
    public void acceptBefore(TcpSession baseTcpVo) {

    }

    @Override
    public void acceptAfter(TcpSession baseTcpVo) {

    }
}
